/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::collections::HashMap;

use actix_web::HttpResponse;
use serde::{Deserialize, Serialize};
use token_management::manager::TokenManager;
use validator::Validate;

use crate::{
    chain::attestation_chain::{AttestationHandler, AttestationContext},
    constants::SERVICE_VERSION,
    entities::{
        attest_request::{AttestRequest, Measurement},
        token::token_trait::{AttesterResult, Token, TokenType},
    },
    error::attestation_error::AttestationError,
    factory::TokenFactory,
};

#[derive(Debug, Serialize, Deserialize)]
pub struct TokenResponse {
    pub node_id: String,
    pub token: String,
}

pub struct DefaultAttestationService;

impl DefaultAttestationService {
    pub fn new() -> Self {
        Self
    }

    /// Process evidence for a single measurement and return evidence token responses
    async fn process_evidence_for_measurement(
        &self,
        measurement: &crate::entities::attest_request::Measurement,
        user_id: &str,
        chain: &dyn AttestationHandler,
        token_instance: Box<dyn Token>,
    ) -> Result<(HashMap<String, AttesterResult>, Box<dyn Token>), AttestationError> {
        let mut evidence_token_responses = HashMap::new();
        let mut current_token_instance = token_instance;

        for evidence in &measurement.evidences {
            let mut context = AttestationContext::new(
                measurement.clone(), 
                evidence.clone(), 
                user_id.to_string(), 
                current_token_instance
            );
            chain.execute(&mut context).await?;
            
            if let Some(evidence_response) = context.evidence_token_response {
                evidence_token_responses.insert(evidence.attester_type.to_string(), evidence_response);
            }
            
            current_token_instance = context.token_instance;
        }

        Ok((evidence_token_responses, current_token_instance))
    }

    /// Generate JWT token from evidence responses and create TokenResponse
    async fn create_token_response(
        &self,
        measurement: &Measurement,
        evidence_token_responses: HashMap<String, AttesterResult>,
        token_instance: Box<dyn Token>,
    ) -> Result<TokenResponse, AttestationError> {
        let token_type = token_instance.create_attestation_response(
            &evidence_token_responses,
            measurement.nonce_type.as_deref().unwrap_or("ignore"),
            &measurement.nonce,
            measurement,
        );
        
        let mut token_json_value = match token_type {
            TokenType::Eat(eat_token) => {
                serde_json::to_value(&eat_token)
                    .map_err(|e| AttestationError::InternalError(format!("Failed to serialize EAT token: {}", e)))?
            },
            TokenType::Ear(ear_token) => {
                serde_json::to_value(&ear_token)
                    .map_err(|e| AttestationError::InternalError(format!("Failed to serialize EAR token: {}", e)))?
            },
        };
        
        let jwt_token = TokenManager::generate_token(&mut token_json_value).await
            .map_err(|_| AttestationError::TokenGenerationError)?;
        
        Ok(TokenResponse {
            node_id: measurement.node_id.clone(),
            token: jwt_token,
        })
    }

    /// Processes an attestation request and generates attestation tokens for all measurements.
    ///
    /// This method is the main entry point for attestation processing. It validates the incoming
    /// request, processes each measurement through the attestation chain, and generates appropriate
    /// tokens based on the evidence provided.
    ///
    /// # Arguments
    ///
    /// * `request` - The attestation request containing measurements and evidence to be processed
    /// * `user_id` - The unique identifier of the user making the attestation request
    /// * `chain` - The attestation handler chain that will process and validate the evidence
    ///
    /// # Returns
    ///
    /// Returns a `Result` containing:
    /// * `Ok(HttpResponse)` - JSON response with service version and generated tokens on success
    /// * `Err(AttestationError)` - Error details if validation or processing fails
    ///
    /// # Errors
    ///
    /// This method can return the following errors:
    /// * `AttestationError::InvalidParameter` - If request validation fails
    /// * `AttestationError::TokenGenerationError` - If token creation fails
    /// * Other attestation errors from the processing chain or evidence validation
    pub async fn process_attestation(
        &self,
        request: AttestRequest,
        user_id: String,
        chain: Box<dyn AttestationHandler>,
    ) -> Result<HttpResponse, AttestationError> {
        if let Err(e) = request.validate() {
            return Err(AttestationError::InvalidParameter(e.to_string()));
        }
        
        let mut all_token_responses = Vec::new();
        
        for measurement in &request.measurements {
            let token_fmt = measurement.token_fmt.clone().unwrap_or("eat".to_string());
            
            // Create token instance once for this measurement
            let token_factory = TokenFactory::new();
            let token_instance = token_factory.create_token(&token_fmt)?;
            
            // Process evidence for this measurement
            let (evidence_token_responses, updated_token_instance) = self.process_evidence_for_measurement(
                measurement, 
                &user_id, 
                chain.as_ref(), 
                token_instance
            ).await?;
            
            // Create token response using the same token instance
            let token_response = self.create_token_response(
                measurement, 
                evidence_token_responses, 
                updated_token_instance
            ).await?;
            
            all_token_responses.push(token_response);
        }
        
        Ok(HttpResponse::Ok().json(serde_json::json!({
            "service_version": SERVICE_VERSION,
            "tokens": all_token_responses
        })))
    }
}